/*  yALB - Yet Another List Builder. Roster Editor for Wargames/tabletop games.
    Copyright (C) 2008-2012  Daniel Ricardo Castro Alvarado

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

#include "yalb.h"
#include "ui_yalb.h"
#include "listsettings.h"
#include "disclaimer.h"
#include "unitlistmodel.h"
#include "unititemdelegate.h"
#include "other/columnheader.h"
#include "other/roster.h"
#include "other/rostermodel.h"
#include "other/rosterelements.h"
#include "optionmodel.h"
#include "other/structreader.h"
#include "unititems.h"

#include "rosterunitprototype.h"

#include <QString>
#include <QMessageBox>
#include <QDebug>
#include <QDir>
#include <QStyle>
#include <QDesktopWidget>
#include <QDesktopServices>
#include <QToolTip>
#include <QSortFilterProxyModel>

// \TODO: Consider "fixing" game and army files just after they are read, to skip some checks while editing the roster
// \TODO: Validation scripts should be easier. What about a ValidationRule class and some counters?
// \TODO: Reduce compile times by using forward declaration when possible.


 Q_DECLARE_METATYPE(RosterUnit*)

yALB::yALB(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::yALB), gameDataPath(qApp->applicationDirPath() + "/gamedata"),
    gameData(), armyData(), itemCategories(&gameData.itemCategories,
                                           &armyData.itemCategories),
    optionCategories(&gameData.optionCategories, &armyData.optionCategories),
    unitCategories(&gameData.unitCategories, &armyData.unitCategories),
    items(&gameData.items, &armyData.items), globalTexts(&gameData.globalTexts,
                                                       &armyData.globalTexts),
    options(&gameData.options, &armyData.options)
{
    ui->setupUi(this);

    setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), QDesktopWidget().availableGeometry()));

    unitItemDialog = new UnitItems(&items, &itemCategories);

    treeItemDelegate = new UnitItemDelegate(this);
    tabBar = new QTabBar(this);
    ui->verticalLayout_4->insertWidget(0,tabBar,0,Qt::AlignRight);
    tabBar->addTab(tr("All"));
    connect(tabBar, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
    ui->rosterWidget->setItemDelegate(treeItemDelegate);
    ui->unitList->setItemDelegate(treeItemDelegate);

    pointsLabel = new QLabel(this);
    pointsLabel->setText("Points: 0/0");
    pointsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pointsLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    pointsLabel->setMargin(3);
    ui->toolBar->addWidget(pointsLabel);

    validationLink = new QCommandLinkButton(this);
    ui->statusBar->addPermanentWidget(validationLink,2);
    validationLink->setIconSize(QSize(16,16));
    validationLink->setFont(qApp->font());
    validationLink->setIcon(QIcon(":/new/img/res/valid.png"));
    validationLink->setText(tr("Valid list"));

    listSettings = new ListSettings(this);

    columnHeader = new ColumnHeader(&gameData.columns, this);

    optionModel = new OptionModel(&optionCategories, this);
    unitListModel = new UnitListModel(&armyData.units, columnHeader);
    roster = new Roster(columnHeader);
    ui->rosterWidget->setModel(roster->model());

    QSortFilterProxyModel *model = new QSortFilterProxyModel(this);
    model->setSourceModel(unitListModel);

    ui->unitList->setModel(model);
    ui->unitOptionList->setModel(optionModel);

    RosterElement::setCurrentRoster(roster);
    RosterUnit::setCurrentItemCollection(&items);
    RosterUnit::setCurrentUnitCollection(&armyData.units);

    connect(&gameData.columns, SIGNAL(columnAdded(Column,QString)), this, SLOT(columnAdded(Column,QString)));
    connect(&armyData.units, SIGNAL(unitAdded(Unit,QString)), this, SLOT(unitAdded(Unit,QString)));
    connect(&gameData.unitCategories, SIGNAL(categoryAdded(GameCategory,QString)), this,
            SLOT(unitCategoryAdded(GameCategory,QString)));
    connect(&armyData.unitCategories, SIGNAL(categoryAdded(GameCategory,QString)), this,
            SLOT(unitCategoryAdded(GameCategory,QString)));

    connect(&armyData.unitCategories, SIGNAL(cleared()), this, SLOT(clearArmyUnitCategories()));
    connect(&gameData.unitCategories, SIGNAL(cleared()), this, SLOT(clearGameUnitCategories()));

    connect(&gameData.columns, SIGNAL(cleared()), columnHeader, SLOT(clearColumns()));
    connect(&armyData.units, SIGNAL(cleared()), unitListModel, SLOT(clearUnits()));

    connect(ui->rosterWidget->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
            this, SLOT(rosterCurrentRowChanged(QModelIndex,QModelIndex)));
    connect(ui->unitList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
            this, SLOT(unitListCurrentRowChanged(QModelIndex,QModelIndex)));
    connect(optionModel, SIGNAL(optionCheckStateToChange(RosterUnit*,QString,Qt::CheckState)),
            this, SLOT(optionCheckStateToChange(RosterUnit*,QString,Qt::CheckState)));

    connect(roster, SIGNAL(pointsChanged(double)), this, SLOT(setPointsText(double)));
    connect(roster, SIGNAL(validationChanged(QSet<QString>)), this, SLOT(updateValidationLink(QSet<QString>)));

    connect(roster->model(), SIGNAL(rowsInserted(QModelIndex, int, int)),
            this, SLOT(rosterRowsInserted(QModelIndex,int,int)));

    connect(validationLink, SIGNAL(clicked()), &validationDialog, SLOT(exec()));


    QDesktopServices::setUrlHandler("gt", this, "showGlobalTextInformation");

    findGamesAndArmies();

    // Load plugins

//    QDir pluginsDir(qApp->applicationDirPath());
//    validatorPlugin = new QPluginLoader(pluginsDir.absoluteFilePath("dwarfsvalidator.dll"));

//    QObject *plugin = validatorPlugin->instance();
//    qDebug() << "Attempting to load plugin";
//    if(plugin) {
//        qDebug() << "Plugin found. Trying to cast.";
//        validator = qobject_cast<validatorClass *>(plugin);
//        if(validator)
//            qDebug() << "Plugin loaded successfully";
//    }

    // Load scripts
    gameValidator = new QScriptEngine(this);
    armyValidator = new QScriptEngine(this);

    /*QDir appPath(qApp->applicationDirPath());
    QString fileName(appPath.absoluteFilePath("example.js"));
    QFile scriptF(fileName);
    scriptF.open(QFile::ReadOnly);
    QTextStream stream(&scriptF);
    QString contents = stream.readAll();
    scriptF.close();
    QScriptValue result = engine->evaluate(contents);
    if(result.isError()) {
        qDebug() << fileName << result.property("lineNumber").toInt32() <<
                result.toString();
    }

    QScriptValue initialize = engine->globalObject().property("initialize");
    result = initialize.call();
    if(result.isError()) {
        qDebug() << fileName << result.property("lineNumber").toInt32() <<
                result.toString();
    }
    if(engine->hasUncaughtException()) {
        int lineNumber = engine->uncaughtExceptionLineNumber();
        qWarning() << "Line " << lineNumber << ":" << result.toString();
    }*/

    //new ModelTest(roster->model(), this);
    //new ModelTest(unitListModel, this);

    QTimer::singleShot(0, this, SLOT(initialize()));
}

yALB::~yALB()
{
    // Done manually to ensure columnHeader is deleted AFTER roster and UnitListModel
    delete roster;
    delete unitListModel;
    delete columnHeader;
    destroyingScript();
    delete ui;
}

void yALB::showGlobalTextInformation(const QUrl &url) {
    QToolTip::showText(QCursor::pos(),globalTexts.get(url.host()).text, this);
}

void yALB::findGamesAndArmies() {
    QDir dir(gameDataPath);

    QStringList entries(dir.entryList(QDir::Files));

    for(int i = 0; i<entries.count(); ++i) {
        if(entries.at(i).endsWith(".ygf")) {
            qDebug() << entries.at(i);
            QList<FileAndName> armies;
            QString gameName(entries.at(i));

            gameName.chop(4); // Delete ".ygf"
            bool hasGameDirectory = dir.cd(gameName);

            QStringList armyEntries(dir.entryList(QDir::Files));
            for(int x = 0; x<armyEntries.size(); ++x) {
                if(armyEntries.at(x).endsWith(".yaf")) {
                    FileAndName army;
                    army.file = gameName + "/" + armyEntries.at(x);
                    army.name = armyEntries.at(x);
                    qDebug() << army.file;
                    armies.append(army);
                }
            }

            FileAndName game;
            game.file = entries.at(i);
            game.name = entries.at(i);
            listSettings->addGame(game, armies);

            if(hasGameDirectory)
                dir.cdUp();
        }
    }
}

void yALB::initialize() {
    Disclaimer d;
    d.exec();
    if(!retrieveListSettings())
        this->close();
}

/* The function retrieveListSettings() attempts to load or create a new list
  according to the data from ListSettings. If the dialog returned Accepted,
  it will show the dialog until a list is opened or created. Otherwise it
  returns false.
*/

bool yALB::retrieveListSettings() {
    bool repeat = false;
    do {
        if(listSettings->exec() == QDialog::Accepted) {
            QString listToOpen = listSettings->getListToOpen();

            if(listToOpen.isEmpty()) {
                // Create a new list

                // \TODO: Use newList() when it's done
                repeat = !loadGameAndArmy(listSettings->getCurrentGameFile(),
                                         listSettings->getCurrentArmyFile());
            } else {
                // Open an existing file
                repeat = !loadList(listToOpen);
            }
        } else
            return false;
    } while (repeat == true);
    return true;
}

bool yALB::loadGameAndArmy(const QString& gameFileName, const QString& armyFileName) {
    if(currentGame != gameFileName) {
        FileInformation information;
        GameFileStructs gameStructs(gameData.toGameFileStructs());
        if(StructReader::readGameFile(gameStructs, information,
                                      gameDataPath + "/" + gameFileName) == StructReader::Success) {
            //armyData.clearAll();

            QHeaderView *header = ui->unitList->horizontalHeader();

            for(int i = 1; i<header->count()-1; ++i)
                header->setResizeMode(i, QHeaderView::Stretch);

            header = ui->rosterWidget->header();

            for(int i = 2; i<header->count()-1;++i)
                header->setResizeMode(i, QHeaderView::Stretch);

            gameData.columns.squeeze();
            gameData.globalTexts.squeeze();
            gameData.itemCategories.squeeze();
            gameData.itemProperties.squeeze();
            gameData.items.squeeze();
            gameData.optionCategories.squeeze();
            gameData.optionProperties.squeeze();
            gameData.options.squeeze();
            gameData.unitCategories.squeeze();
            gameData.unitProperties.squeeze();

            informationDialog.setGameName(information.name);
            informationDialog.setGameVersionAndAuthor(information.version, information.creator);
            informationDialog.setGameWebsite(information.website);
            informationDialog.setGameComments(information.comments);

            listSettings->setCurrentGameFile(gameFileName);

            currentGame = gameFileName;

            return loadArmyFile(armyFileName, false);
        } else
            return false;
    } else if(armyFileName != currentArmy) {
        return loadArmyFile(armyFileName, true);
    } else
        return true;

}

bool yALB::loadArmyFile(const QString& fileName, bool clearAll) {
    QHash<QString, QHash<QString, QString> > inheritedOptions; // QHash<unitKey, QHash<optionKey, inheritedKey> >

    ArmyFileStructs armyStructs(armyData.toArmyFileStructs());

    QHash<QString, InheritedCategoryData > unitCategoryData;
    QHash<QString, InheritedCategoryData > itemCategoryData;
    QHash<QString, InheritedCategoryData > optionCategoryData;
    QHash<QString, InheritedItemData > itemData;
    QHash<QString, InheritedOptionData> optionData;

    armyStructs.inheritedItemCategoryData = &itemCategoryData;
    armyStructs.inheritedItemData = &itemData;
    armyStructs.inheritedOptionCategoryData = &optionCategoryData;
    armyStructs.inheritedOptionData = &optionData;
    armyStructs.inheritedUnitCategoryData = &unitCategoryData;

    armyStructs.clearAll();

    FileInformation information;

    bool result = false;

    if(StructReader::readArmyFile(armyStructs, information,
                                  gameDataPath + "/" + fileName, this,
                                  &inheritedOptions, clearAll) == StructReader::Success) {
        QHash<QString, QHash<QString, QString> >::const_iterator i;

        // Set up shared data for inherited options
        // \NOTE: This will add new data if it didn't exist

        for(i=inheritedOptions.constBegin(); i != inheritedOptions.constEnd(); ++i) {
            QHash<QString, QString>::const_iterator x;
            const QHash<QString, QString> &value = i.value();

            for(x=value.constBegin(); x!=value.constEnd(); ++x) {
                Unit *unit = armyData.units.getRealElement(i.key());
                if(unit) {
                    UnitOption* option = unit->options.getRealElement(x.key());
                    // \NOTE: How should I delete ObjectAction* when it's shared with "derived" options?
                    if(option && options.contains(x.value())) {
                        const Actions& act = options.getRealElement(x.value())->actions;
                        if(option->actions.isEmpty())
                            option->actions = act;
                        else {
                            option->actions = act + option->actions;
                        }
                    }
                }
            }
        }

        // Set up army modifications for game data

        QHash<QString, InheritedCategoryData>::const_iterator iuc;
        QHash<QString, InheritedCategoryData>::const_iterator iic;
        QHash<QString, InheritedCategoryData>::const_iterator ioc;
        QHash<QString, InheritedItemData>::const_iterator ii;
        QHash<QString, InheritedOptionData>::const_iterator io;

        CategoryDataHash defaultItemLimits; // For unit set up

        // Unit categories
        for(iuc=unitCategoryData.constBegin(); iuc!=unitCategoryData.constEnd(); ++iuc) {
            armyData.unitCategories.add(getModifiedCategory(iuc.value(), iuc.key(), gameData.unitCategories), iuc.key());
        }

        // Item categories
        for(iic=itemCategoryData.constBegin(); iic!=itemCategoryData.constEnd(); ++iic) {
            GameCategory newCategory = getModifiedCategory(iic.value(), iic.key(), gameData.itemCategories);
            armyData.itemCategories.add(newCategory, iic.key());

            defaultItemLimits[iic.key()].numericLimit = newCategory.getNumericLimit();
            defaultItemLimits[iic.key()].percentageLimit = newCategory.getPercentageLimit();
            defaultItemLimits[iic.key()].pointLimit = newCategory.getPointLimit();
        }
        defaultItemLimits.squeeze();

        // Option categories
        for(ioc=optionCategoryData.constBegin(); ioc!=optionCategoryData.constEnd(); ++ioc) {
            armyData.optionCategories.add(getModifiedCategory(ioc.value(), ioc.key(), gameData.optionCategories), ioc.key());
        }

        // Items
        for(ii=itemData.constBegin(); ii!=itemData.constEnd(); ++ii) {
            GameItem item = gameData.items.get(ii.key());
            const InheritedItemData &data = ii.value();
            if(data.cost != -1)
                item.setCost(data.cost);
            if(data.maxNumber != -1)
                item.setMaximumNumber(data.maxNumber);
            armyData.items.add(item, ii.key());
        }

        // Options
        for(io=optionData.constBegin(); io!=optionData.constEnd(); ++io) {
            UnitOption option = gameData.options.get(io.key());
            const InheritedOptionData &data = io.value();

            if(data.cost >= 0)
                option.setCost(data.cost);
            armyData.options.add(option, io.key());
        }

        // Set up unit item limits
        // We use a CategoryDataHash to avoid duplicating the same data thanks to Copy-on-write
        // semantics used in QHash

        QStringList unitKeys(armyData.units.keys());
        for(int i = 0; i<unitKeys.size(); ++i) {
            Unit *u = armyData.units.getRealElement(unitKeys.at(i));
            CategoryDataHash oldData = u->itemLimits;
            u->itemLimits = defaultItemLimits;
            for(CategoryDataHash::const_iterator it = oldData.constBegin();
                it != oldData.constEnd(); ++it) {
                const InheritedCategoryData &d = it.value();
                if(d.numericLimit != (uint)-1)
                    u->itemLimits[it.key()].numericLimit = d.numericLimit;
                if(d.percentageLimit != (uint)-1)
                    u->itemLimits[it.key()].percentageLimit = d.percentageLimit;
                if(d.pointLimit != (uint)-1)
                    u->itemLimits[it.key()].pointLimit = d.pointLimit;
            }
        }

        armyData.globalTexts.squeeze();
        armyData.itemCategories.squeeze();
        armyData.items.squeeze();
        armyData.optionCategories.squeeze();
        armyData.options.squeeze();
        armyData.unitCategories.squeeze();
        armyData.units.squeeze();

        unitItemDialog->armyFileChanged();

        informationDialog.setArmyName(information.name);
        informationDialog.setArmyVersionAndAuthor(information.version, information.creator);
        informationDialog.setArmyWebsite(information.website);
        informationDialog.setArmyComments(information.comments);

        currentArmy = fileName;

        informationDialog.exec();

        QString fileName(gameDataPath % "/" % "example.vs");
        QFile scriptF(fileName);
        scriptF.open(QFile::ReadOnly);
        QTextStream stream(&scriptF);
        QString contents = stream.readAll();
        scriptF.close();
        QScriptValue r = armyValidator->evaluate(contents);
        if(r.isError()) {
            qDebug() << fileName << r.property("lineNumber").toInt32() <<
                        r.toString();
        }

        QScriptValue initialize = armyValidator->globalObject().property("initialize");
        r = initialize.call();
        if(r.isError()) {
            qDebug() << fileName << r.property("lineNumber").toInt32() <<
                        r.toString();
        }
        if(armyValidator->hasUncaughtException()) {
            int lineNumber = armyValidator->uncaughtExceptionLineNumber();
            qWarning() << "Line " << lineNumber << ":" << r.toString();
        }
        armyValidator->globalObject().setProperty("roster", armyValidator->newQObject(roster));
        armyValidator->setDefaultPrototype(qMetaTypeId<RosterUnit*>(),
                                           armyValidator->newQObject(new RosterUnitPrototype(this)));
        qScriptConnect(roster, SIGNAL(unitAdded(RosterUnit*)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onUnitAdded"));
        qScriptConnect(roster, SIGNAL(unitAboutToBeRemoved(RosterUnit*)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onUnitAboutToBeRemoved"));
        qScriptConnect(roster, SIGNAL(unitCostChanged(RosterUnit*,float)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onUnitCostChanged"));
        qScriptConnect(roster, SIGNAL(unitSizeChanged(RosterUnit*)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onUnitSizeChanged"));
        qScriptConnect(roster, SIGNAL(itemAdded(RosterUnit*,QString,quint8)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onItemAdded"));
        qScriptConnect(roster, SIGNAL(itemLimitsChanged(RosterUnit*)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onItemLimitsChanged"));
        qScriptConnect(roster, SIGNAL(itemRemoved(RosterUnit*,QString,quint8)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onItemRemoved"));
        qScriptConnect(roster, SIGNAL(optionActivated(RosterUnit*,QString)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onOptionActivated"));
        qScriptConnect(roster, SIGNAL(optionDeactivated(RosterUnit*,QString)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onOptionDeactivated"));
        qScriptConnect(roster, SIGNAL(pointsChanged(double)), armyValidator->globalObject(),
                       armyValidator->globalObject().property("onPointsChanged"));


        result = true;
    }

    return result;
}

bool yALB::loadList(const QString& fileName) {
    // \TODO: Implement loadList() function
    return false;
}

GameCategory yALB::getModifiedCategory(const InheritedCategoryData& data, const QString& key,
                                const CategoryCollection& categories) {
    GameCategory category = categories.get(key);
    if(data.numericLimit != -1)
        category.setNumericLimit(data.numericLimit);
    if(data.percentageLimit != -1)
        category.setPercentageLimit(data.percentageLimit);
    if(data.pointLimit != -1)
        category.setPointLimit(data.pointLimit);
    return category;

}

void yALB::setSaved(bool saved) {
    if(isSaved != saved) {
        isSaved = saved;
        ui->actionSaveList->setEnabled(!saved);
    }
}

bool yALB::unsavedFileMessage() {
    // This function returns true if the application can proceed or returns false if it has to abort the operation
    if(!isSaved) {
        QMessageBox::StandardButton result = QMessageBox::warning(this, tr("List not saved"),
                                                                  tr("The current list has not been saved. Would you like to save it before proceeding?"),
                                                                  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
        if(result == QMessageBox::Yes) {
            on_actionSaveList_triggered();
            return true;
        } else if(result == QMessageBox::Cancel)
            return false;
        else
            return true;
    } else
        return true;
}

QStringList yALB::itemToStringList(QTreeWidgetItem *item) {
    QStringList texts;
    if(item) {
        int i;
        for(i = 0; i<item->columnCount();i++)
            texts << item->text(i);
        return texts;

    }
    return texts;
}

void yALB::destroyingScript() {
    if(armyValidator) {
        qDebug() << "Calling destroy() in army script";
        QScriptValue destroy = armyValidator->globalObject().property("destroy");
        QScriptValue result = destroy.call(QScriptValue());
        if(result.isError()) {
            qDebug() << "destroy()" << result.property("lineNumber").toInt32() <<
                    result.toString();
        }
    }

    if(gameValidator) {
        qDebug() << "Calling destroy() in game script";
        QScriptValue destroy = gameValidator->globalObject().property("destroy");
        QScriptValue result = destroy.call(QScriptValue());
        if(result.isError()) {
            qDebug() << "destroy()" << result.property("lineNumber").toInt32() <<
                    result.toString();
        }
    }
}

void yALB::unitCategoryAdded(const GameCategory& element, const QString &key) {
    tabBar->addTab(element.getName());
    tabBar->setTabData(tabBar->count() -1, QVariant(key));
}

void yALB::clearGameUnitCategories() {
    for(int i = tabBar->count()-1; i>=1; --i) {
        if(armyData.unitCategories.contains(tabBar->tabData(i).toString()))
            continue;
        tabBar->removeTab(i);
    }
}

void yALB::clearArmyUnitCategories() {
    for(int i = tabBar->count()-1; i>=1; --i) {
        if(!armyData.unitCategories.contains(tabBar->tabData(i).toString()))
            break;
        tabBar->removeTab(i);
    }
}

void yALB::unitAdded(const Unit& unit, const QString &key) {
    Q_UNUSED(unit)
    Unit *u = armyData.units.getRealElement(key);
    QHash<QString, QString>::iterator i = u->cells.begin();

    while (i != u->cells.end()) {
        // Set default value for empty cells
        if(i.value().isEmpty())
            i.value() = gameData.columns.get(i.key()).defaultValue;

        // Delete cells for non existing columns
        if(!gameData.columns.contains(i.key()))
            i = u->cells.erase(i);
        else
            ++i;
    }

    if(!u->flags.testFlag(Unit::Hidden)) {
        unitListModel->addUnit(key);
    }
}

void yALB::columnAdded(const Column& column, const QString &key) {
    Q_UNUSED(column)
    columnHeader->addColumn(key);
}

void yALB::tabChanged(int index)
{
    QString tabCategory = tabBar->tabData(index).toString();
    for(int i = 0; i<unitListModel->rowCount(); ++i) {
        QString category = armyData.units.getRealElement(unitListModel->getUnitKey(i))->category;
        if(index == 0 || category == tabCategory)
            ui->unitList->setRowHidden(i, false);
        else
            ui->unitList->setRowHidden(i, true);
    }
}

void yALB::on_addButton_clicked()
{
    // \NOTE: addButton_clicked() is performing only test cases
    //Just testing...

    QModelIndex currentIndex = ui->rosterWidget->currentIndex();

    if(!currentIndex.isValid())
        return;

    RosterElement* element = roster->model()->getElementFromIndex(currentIndex);
    RosterUnit *unit = new RosterUnit("mount", NULL);
    RosterElement* child = new RosterElement(NULL);
    child->setName("Item");
    roster->addRosterElement(unit, element);
    roster->addRosterElement(child, element);

    /*if(validator)
        validator->validateUnit(0,*(ui->list->topLevelItem(0)),validationErrors);*/
    if(gameValidator && armyValidator) {
        qDebug() << "Calling validateUnit() in script";
        /*QScriptValue validateUnit = engine->globalObject().property("validateUnit");
        QScriptValueList args;
        args << 0 << engine->toScriptValue(itemToStringList(ui->list->topLevelItem(0)));
        QScriptValue result = validateUnit.call(QScriptValue(), args);
        if(result.isError()) {
            qDebug() << "validateUnit()" << result.property("lineNumber").toInt32() <<
                    result.toString();
        }*/
    }
}

void yALB::on_actionDelete_triggered()
{
    // \TODO: Implement delete trigger
    QModelIndex current(ui->rosterWidget->currentIndex());
    if(current.isValid())
        roster->removeRosterElement(current.row());
}

void yALB::on_actionSaveList_triggered()
{
    // \TODO: Implement save trigger
}

void yALB::on_unitList_doubleClicked(const QModelIndex &index)
{
    if(!index.isValid())
        return;

    QString key = unitListModel->getUnitKey(index.row());
    RosterUnit *unit = new RosterUnit(key, NULL);
    roster->addRosterElement(unit);
}

void yALB::rosterCurrentRowChanged(const QModelIndex &current, const QModelIndex &previous) {
    Q_UNUSED(previous)

    RosterUnit *unit = NULL;
    RosterElement *element = roster->model()->getElementFromIndex(current);

    if(element && element->type() == 1)
        unit = static_cast<RosterUnit*>(element);
    optionModel->setSourceUnit(unit);
    unitItemDialog->setSourceUnit(unit);

    if(!current.isValid())
        return;
    ui->unitOptionList->expandAll();

    ui->stackedWidget->setCurrentIndex(1);

    ui->unitList->clearSelection();
    ui->unitList->setCurrentIndex(QModelIndex());
}

void yALB::unitListCurrentRowChanged(const QModelIndex &current, const QModelIndex &previous) {
    Q_UNUSED(previous)

    if(!current.isValid())
        return;

    ui->stackedWidget->setCurrentIndex(0);
    ui->unitDescriptionLabel->setText(armyData.units.getRealElement(unitListModel->getUnitKey(current.row()))->description);

    ui->rosterWidget->clearSelection();
    ui->rosterWidget->setCurrentIndex(QModelIndex());
}

void yALB::optionCheckStateToChange(RosterUnit *unit, const QString &optionKey, Qt::CheckState targetState) {
    if(targetState == Qt::Checked) {
        unit->activateOption(optionKey);
    } else
        unit->deactivateOption(optionKey);
}

void yALB::on_itemsButton_clicked()
{
    unitItemDialog->exec();
}

void yALB::setPointsText(double points) {
    pointsLabel->setText(tr("Points: %1/%2").arg(points).arg(0));
}

void yALB::updateValidationLink(const QSet<QString>& errors) {
    if(errors.size() > 0) {
        validationLink->setIcon(QIcon(":/new/img/res/errors.png"));
        validationLink->setText(tr("Your list has %1 error(s)").arg(errors.size()));
    } else {
        validationLink->setIcon(QIcon(":/new/img/res/valid.png"));
        validationLink->setText(tr("Valid list"));
    }
    validationDialog.setSource(errors);
}

void yALB::rosterRowsInserted(const QModelIndex &parent, int start, int end) {
    ui->rosterWidget->expand(parent);
}
